﻿/**
 * Copyright (C) 2012 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGuice.Inject.Spi;
using NGuice.Inject.Internal.Util;

namespace NGuice.Inject.Internal
{
    //complete!
    /// <summary>
    /// 一个部分初始化的注入器，InternalInjectorCreator使用此类构建注入器树
    /// </summary>
    public sealed class InjectorShell
    {
        private readonly IList<IElement> elements;

        private readonly InjectorImpl injector;

        private readonly IPrivateElements privateElements;

        private InjectorShell(Builder builder, IList<IElement> elements, InjectorImpl injector)
        {
            this.privateElements = builder.privateElements;
            this.elements = elements;
            this.injector = injector;
        }

        internal InjectorImpl Injector
        {
            get
            {
                return injector;
            }
        }

        internal IList<IElement> Elements
        {
            get
            {
                return elements;
            }
        }

        internal class Builder
        {
            private readonly List<IElement> elements = Lists.NewArrayList<IElement>();

            private readonly List<IModule> modules = Lists.NewArrayList<IModule>();

            private IState state;

            private InjectorImpl parent;

            private InjectorImpl.InjectorOptions options;

            private Stage stage;

            //null unless this exists in a {@link Binder#newPrivateBinder private environment}
            internal PrivateElementsImpl privateElements;

            internal Builder Stage(Stage stage)
            {
                this.stage = stage;
                return this;
            }

            internal Builder Parent(InjectorImpl parent)
            {
                this.parent = parent;
                this.state = new InheritingState(parent.state);
                this.options = parent.options;
                this.stage = options.stage;
                return this;
            }

            internal Builder PrivateElements(IPrivateElements privateElements)
            {
                this.privateElements = (PrivateElementsImpl)privateElements;
                this.elements.AddRange(privateElements.Elements);
                return this;
            }

            internal void AddModules(IEnumerable<IModule> modules)
            {
                foreach (IModule module in modules)
                {
                    this.modules.Add(module);
                }
            }

            internal Stage GetStage()
            {
                return options.stage;
            }

            /** Synchronize on this before calling {@link #build}. */
            internal object Lock()
            {
                return State.Lock();
            }

            /// <summary>
            /// 为当前模块创建并返回注入器shell。如果模块包含<code>Binder#NewPrivateBinder</code>私有环境，
            /// 多个shell将返回。主要的注入器将在列表中的第一个
            /// </summary>
            /// <param name="bindingProcessor"></param>
            /// <param name="stopwatch"></param>
            /// <param name="errors"></param>
            /// <returns></returns>
            internal IList<InjectorShell> Build(BindingProcessor bindingProcessor, Stopwatch stopwatch, Errors errors)
            {
                Preconditions.CheckState(stage != null, "Stage not initialized");
                Preconditions.CheckState(privateElements == null || parent != null, "PrivateElements with no parent");
                Preconditions.CheckState(state != null, "no state. Did you remember to lock() ?");

                // bind Stage and Singleton if this is a top-level injector
                if (parent == null)
                {
                    modules.Insert(0, new RootModule(stage));
                }
                //elements.AddRange(Spi.Elements.GetElements(stage, modules));
                foreach (IElement e in Spi.Elements.GetElements(stage, modules))
                {
                    elements.Add(e);
                }

                // Look for injector-changing options
                InjectorOptionsProcessor optionsProcessor = new InjectorOptionsProcessor(errors);
                optionsProcessor.Process(null, elements);
                options = optionsProcessor.GetOptions(stage, options);

                InjectorImpl injector = new InjectorImpl(parent, state, options);
                if (privateElements != null)
                {
                    privateElements.InitInjector(injector);
                }

                // add default type converters if this is a top-level injector
                if (parent == null)
                {
                    new TypeConverterBindingProcessor(errors).PrepareBuiltInConverters(injector);
                }

                stopwatch.ResetAndLog("Module execution");

                new MessageProcessor(errors).Process(injector, elements);

                /*if[AOP]*/
                InterceptorBindingProcessor interceptors = new InterceptorBindingProcessor(errors);
                interceptors.Process(injector, elements);
                stopwatch.ResetAndLog("Interceptors creation");
                /*end[AOP]*/

                new TypeListenerBindingProcessor(errors).Process(injector, elements);
                IList<TypeListenerBinding> listenerBindings = injector.state.TypeListenerBindings;
                injector.membersInjectorStore = new MembersInjectorStore(injector, listenerBindings);
                stopwatch.ResetAndLog("TypeListeners creation");

                new ScopeBindingProcessor(errors).Process(injector, elements);
                stopwatch.ResetAndLog("Scopes creation");

                new TypeConverterBindingProcessor(errors).Process(injector, elements);
                stopwatch.ResetAndLog("Converters creation");

                InjectorShell.BindInjector(injector);
                InjectorShell.BindLogger(injector);
                bindingProcessor.Process(injector, elements);
                stopwatch.ResetAndLog("Binding creation");

                List<InjectorShell> injectorShells = Lists.NewArrayList<InjectorShell>();
                injectorShells.Add(new InjectorShell(this, elements, injector));

                // recursively build child shells
                PrivateElementProcessor processor = new PrivateElementProcessor(errors);
                processor.Process(injector, elements);
                foreach (Builder builder in processor.GetInjectorShellBuilders())
                {
                    injectorShells.AddRange(builder.Build(bindingProcessor, stopwatch, errors));
                }
                stopwatch.ResetAndLog("Private environment creation");

                return injectorShells;
            }

            private IState State
            {
                get
                {
                    if (state == null)
                    {
                        state = new InheritingState(NoneState.INSTANCE);
                    }
                    return state;
                }
            }
        }

        /// <summary>
        /// Injector注入器是一个特殊例子，因为我们允许父子注入器都具有一个指定key的binding
        /// </summary>
        /// <param name="injector"></param>
        private static void BindInjector(InjectorImpl injector)
        {
            Key<Injector> key = Key<Injector>.Get();
            InjectorFactory injectorFactory = new InjectorFactory(injector);
            injector.state.PutBinding(key,
                new ProviderInstanceBindingImpl<Injector>(injector, key, SourceProvider.UNKNOWN_SOURCE,
                    injectorFactory, Scoping.UNSCOPED, injectorFactory,
                    ImmutableSet<InjectionPoint>.Of()));
        }

        private class InjectorFactory : InternalFactory<Injector>, IProvider<Injector>
        {
            private readonly Injector injector;

            internal InjectorFactory(Injector injector)
            {
                this.injector = injector;
            }

            public Injector Get(Errors errors, InternalContext context, Dependency dependency, bool linked)
            {
                return injector;
            }

            public Injector Get()
            {
                return injector;
            }

            public override string ToString()
            {
                return "Provider<Injector>";
            }

            object IProvider.Get()
            {
                return Get();
            }

            object InternalFactory.Get(Errors errors, InternalContext context, Dependency dependency, bool linked)
            {
                return Get(errors, context, dependency, linked);
            }
        }

        /// <summary>
        /// NLog.Logger是一个特殊的例子，因为他知晓被注入成员的注入点。他是执行这些的仅有的binding
        /// </summary>
        /// <param name="injector"></param>
        private static void BindLogger(InjectorImpl injector)
        {
            Key<NLog.Logger> key = Key<NLog.Logger>.Get();
            LoggerFactory loggerFactory = new LoggerFactory();
            injector.state.PutBinding(key,
                new ProviderInstanceBindingImpl<NLog.Logger>(injector, key,
                    SourceProvider.UNKNOWN_SOURCE, loggerFactory, Scoping.UNSCOPED,
                    loggerFactory, ImmutableSet<InjectionPoint>.Of()));
        }

        private class LoggerFactory : InternalFactory<NLog.Logger>, IProvider<NLog.Logger>
        {
            public NLog.Logger Get(Errors errors, InternalContext context, Dependency dependency, bool linked)
            {
                InjectionPoint injectionPoint = dependency.InjectionPoint;
                return injectionPoint == null
                    ? NLog.LogManager.GetLogger("AnonymousLogger")//匿名日志
                    : NLog.LogManager.GetLogger(injectionPoint.Member.DeclaringType.FullName);
            }

            public NLog.Logger Get()
            {
                return NLog.LogManager.GetLogger("AnonymousLogger");
            }

            public override string ToString()
            {
                return "Provider<NLog.Logger>";
            }

            object IProvider.Get()
            {
                return Get();
            }

            object InternalFactory.Get(Errors errors, InternalContext context, Dependency dependency, bool linked)
            {
                return Get(errors, context, dependency, linked);
            }
        }

        private class RootModule : IModule
        {
            readonly Stage stage;

            internal RootModule(Stage stage)
            {
                this.stage = Preconditions.CheckNotNull(stage, "stage");
            }

            public void Configure(IBinder binder)
            {
                binder = binder.WithSource(SourceProvider.UNKNOWN_SOURCE);
                binder.Bind<Stage>().ToInstance(stage);
                binder.BindScope<SingletonAttribute>(Scopes.SINGLETON);
                //binder.BindScope(javax.inject.Singleton.class, SINGLETON);
            }
        }
    }
}
